package _.javax.mail.util;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.mail.Address;
import javax.mail.BodyPart;
import javax.mail.Flags;
import javax.mail.Flags.Flag;
import javax.mail.Folder;
import javax.mail.Message;
import javax.mail.Message.RecipientType;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.Part;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;

import org.budo.support.java.collection.wrapper.NiceToStringListWrapper;
import org.budo.support.lang.util.ArrayUtil;
import org.budo.support.lang.util.IoUtil;
import org.budo.support.lang.util.ListUtil;
import org.budo.support.lang.util.StringUtil;
import org.budo.support.lang.util.UuidUtil;
import org.budo.support.slf4j.Slf4j;
import org.slf4j.Logger;
import org.budo.mail.javax.MailAttachment;
import org.budo.mail.javax.internet.util.MimeUtil;

import com.sun.mail.imap.IMAPFolder;
import com.sun.mail.pop3.POP3Folder;

import _.mail.javax.MailAttachmentImpl;

/**
 * @author li
 * @version 1 (2014年10月31日 下午5:58:27)
 * @since Java7
 */
public class MessageUtil_2 {
    private static final Logger log = Slf4j.getLogger();

    public static List<String> getTo(Message message) {
        return getRecipients(message, RecipientType.TO);
    }

    public static List<String> getCc(Message message) {
        return getRecipients(message, RecipientType.CC);
    }

    public static List<String> getBcc(Message message) {
        return getRecipients(message, RecipientType.BCC);
    }

    public static List<String> getRecipients(Message message, RecipientType recipientType) {
        try {
            return addressToStringList(message.getRecipients(recipientType));
        } catch (Throwable e) {
            if (e instanceof AddressException) {
                log.error("#63 getRecipients error AddressException, e=" + e);
                return ListUtil.toList();
            }

            if (("" + e).contains("Folder is not Open")) {
                log.error("#68 getRecipients error Folder is not Open, e=" + e);
                return ListUtil.toList();
            }

            throw new RuntimeException(e);
        }
    }

    public static Address[] getFromAddresses(Message message) {
        try {
            return message.getFrom();
        } catch (MessagingException e) {
            if (StringUtil.contains("" + e, "MessageRemovedException: can't retrieve message")) {
                return new Address[0];
            } else {
                throw new RuntimeException(e);
            }
        }
    }

    public static List<String> getFrom(Message message) {
        try {
            Address[] from = getFromAddresses(message);
            return addressToStringList(from);
        } catch (Exception e) {
            if (StringUtil.contains("" + e, "IllegalStateException: Folder is not Open")) {
                log.error("#94 ReceiveMail.getFrom() error，e=" + e);

                return new ArrayList<String>();
            } else {
                throw e instanceof RuntimeException ? (RuntimeException) e : new RuntimeException(e);
            }
        }
    }

    @SuppressWarnings("unchecked")
    public static List<String> addressToStringList(Address... addresses) {
        List<String> values = new ArrayList<String>();
        if (null != addresses) {
            for (Address address : addresses) {
                values.add(((InternetAddress) address).getAddress());
            }
        }
        return new NiceToStringListWrapper(values);
    }

    public static String addressToString(Address... addresses) {
        List<String> addressToStringList = addressToStringList(addresses);
        return StringUtil.join(addressToStringList, ",");
    }

    public static void addRecipients(Message message, RecipientType recipientType, String... addresses) {
        try {
            for (String each : addresses) {
                message.addRecipient(recipientType, new InternetAddress(each));
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static List<MailAttachment> getAttachments(Message message) {
        List<MailAttachment> attachments = new ArrayList<MailAttachment>();

        try {
            Object messageContent = message.getContent();
            if (messageContent instanceof Multipart) {
                Multipart multipart = (Multipart) messageContent;
                int multipartCount = multipart.getCount();

                for (int i = 0; i < multipartCount; i++) {
                    BodyPart bodyPart = multipart.getBodyPart(i);

                    if (StringUtil.equals(bodyPart.getDisposition(), Part.ATTACHMENT)) {
                        InputStream inputStream = bodyPart.getInputStream();
                        byte[] bytes = IoUtil.toByteArray(inputStream);
                        String fileName = MimeUtil.decodeText(bodyPart.getFileName()); // 正常方式取附件名

                        if (StringUtil.isEmpty(fileName)) { // 如果附件名未取到
                            fileName = generateMailAttachmentFileName(bodyPart); // 则生成一个附件名
                        }

                        attachments.add(new MailAttachmentImpl(fileName, bytes));

                        IoUtil.close(inputStream);
                    }
                }
            }
        } catch (Throwable e) {
            log.error("#156 获取邮件附件失败 " + e, e);
        }

        return attachments;
    }

    /**
     * 如果未取到邮件附件名,则生成一个
     */
    private static String generateMailAttachmentFileName(BodyPart bodyPart) throws MessagingException {
        String[] contentTypes = bodyPart.getHeader("Content-Type");
        if (null != contentTypes) {
            for (String contentType : contentTypes) {
                if (null != contentType) {
                    if (StringUtil.equalsIgnoreCase(contentType.trim(), "message/rfc822".trim())) { // 是 message/rfc822
                        return UuidUtil.randomUuid() + ".eml";
                    }
                }
            }
        }

        log.error("#178 generateMailAttachmentFileName, contentTypes=" + StringUtil.join(contentTypes));
        return UuidUtil.randomUuid() + ".ewei_mail_attachment";
    }

    public static Flags getFlags(Message message) {
        try {
            return message.getFlags();
        } catch (MessagingException e) {
            throw new RuntimeException(e);
        }
    }

    public static void setFlag(Message message, Flag flag, Boolean value) {
        try {
            message.setFlag(flag, value); // 直接删除, 找不回来
        } catch (MessagingException e) {
            throw new RuntimeException(e);
        }
    }

    public static Map<String, byte[]> getImagesInMessageBody(Message message) {
        return getImagesInMessageBody_1(message);
    }

    /**
     * @see _.mail.javax.ReceiveMailImpl#messageToMap(Object)
     */
    private static Map<String, byte[]> getImagesInMessageBody_1(Message message) {
        try {
            return getImagesInMessageBody_2(message);
        } catch (Throwable e) {
            log.error("#209 getImagesInMessageBody_1 error, e=" + e, e);
            return new HashMap<String, byte[]>();
        }
    }

    private static Map<String, byte[]> getImagesInMessageBody_2(Object message) throws Throwable {
        if (null == message) {
            log.error("#216 message is null getImagesInMessageBody_2");
            return new HashMap<String, byte[]>();
        }

        Map<String, byte[]> map = new HashMap<String, byte[]>();

        if (message instanceof Message) {
            Object content = ((Message) message).getContent();
            Map<String, byte[]> map_1 = getImagesInMessageBody_2(content); // 递归
            map.putAll(map_1);
        } else if (message instanceof Multipart) {
            Multipart multipart = (Multipart) message;
            int multipartCount = multipart.getCount();

            for (int i = 0; i < multipartCount; i++) {
                BodyPart bodyPart = multipart.getBodyPart(i);
                Map<String, byte[]> map_2 = getImagesInMessageBody_2(bodyPart); // 递归
                map.putAll(map_2);
            }
        } else if (message instanceof BodyPart) {
            BodyPart bodyPart = (BodyPart) message;
            String contentId = ArrayUtil.first(bodyPart.getHeader("Content-ID"));
            Object content = bodyPart.getContent();

            if (!StringUtil.isEmpty(contentId) // 本级有 Content-ID 且 下级是 InputStream
                    && null != content && content instanceof InputStream) {
                String key = contentId.replace("<", "").replace(">", "");
                byte[] dataBytes = IoUtil.toByteArray(content); // 处理图片
                map.put(key, dataBytes);
            } else { // 继续递归，处理 BodyPart 内包含 Multipart 的情况
                Map<String, byte[]> map_3 = getImagesInMessageBody_2(content); // 递归
                map.putAll(map_3);
            }
        } else if (message instanceof InputStream) { // 会进入这里 // BASE64DecoderStream
            log.debug("#251 doGetImagesInMessageBody_新版本, message type else 1, message=" + message + ", message.type=" + message.getClass().getName());
        } else if (message instanceof String) { // 会进入这里 // 2017-6-16 16:14:26
            log.debug("#253 doGetImagesInMessageBody_新版本, message type else 2, message=" + message + ", message.type=" + message.getClass().getName());
        } else { // 不应该进入
            log.error("doGetImagesInMessageBody_新版本, message type else 3, message=" + message + ", message.type=" + message.getClass().getName());
        }

        return map;
    }

    public static String getUid(Message message) {
        try {
            Folder folder = message.getFolder();
            if (folder instanceof POP3Folder) {
                return ((POP3Folder) folder).getUID(message);
            } else if (folder instanceof IMAPFolder) {
                return "" + ((IMAPFolder) folder).getUID(message);
            } else {
                throw new RuntimeException("unkonwn message type " + message.getClass().getName() + ", folder type " + folder.getClass().getName());
            }
        } catch (MessagingException e) {
            throw new RuntimeException(e);
        }
    }

    public static String getSubject(Message message) {
        try {
            return message.getSubject();
        } catch (MessagingException e) {
            throw new RuntimeException(e);
        }
    }
}